home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / clonecd / September 93.img / Archives / Fun, Tricks & Hacks / Parrot10a5 / source / Confusion.c next >
Text File  |  1992-10-31  |  20KB  |  830 lines

  1. /**********
  2.  * Sound Confusion
  3.  *
  4.  *        Copyright © 1992 Bernie Bernstein
  5.  *        9/11/92 - 10/14/92
  6.  ***********/
  7.  
  8.  
  9. #include    <GestaltEqu.h>
  10. #include    <Memory.h>
  11. #include    <OSEvents.h>
  12. #include    <Sound.h>
  13. #include    <SoundInput.h>
  14. #include    <StdIO.h>
  15. #include    <Types.h>
  16.  
  17. #include "Confusion.h"
  18.  
  19. /* how much memory should the program reserve after making the buffers? */
  20. #define kExtraMem        0x5000
  21.  
  22.  
  23. /* reset the channels every kResetInterval seconds */
  24. #define kResetInterval    30
  25.  
  26.  
  27. /*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
  28. The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
  29. pointing at a SoundHeader Record and not an 'snd ' resource header. */
  30. #define kHeaderSize        20
  31.  
  32. typedef struct {
  33.     short        OnOff;
  34.     short         Level;
  35.     short         Length;
  36. } Level;
  37.  
  38.  
  39. Boolean    inStereo;
  40. short gThreshold;
  41. long gBuffSize;
  42. SCGlobals *globs;
  43. BufInfo gExtraBuffer;
  44. Boolean gPlayNRecord;
  45.  
  46.  
  47. /***
  48.  * BufPlay
  49.  *        This routine takes an snd buffer and a sound channel and turns the information
  50.  *        into a bufferCmd to the channel.
  51.  ***/
  52. void BufPlay (Handle Buf, SndChannelPtr    channel)
  53. {
  54.     SndCommand        localSndCmd;
  55.     OSErr            err;
  56.     
  57.     localSndCmd.cmd = bufferCmd;
  58.     localSndCmd.param1 = 0;
  59.     localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
  60.     
  61.     err = SndDoCommand (channel, &localSndCmd, false);
  62.     if (err != noErr)
  63.         AlertUser(err, iPlaying);
  64.     return;
  65. }
  66.  
  67.  
  68. /***
  69.  * SetUpSounds
  70.  *        SetUpSounds is a routine which takes a buffer handle, sound headers size and
  71.  *        sample rate value and turns it into a snd buffer with the proper header.
  72.  *        It then returns the buffer handle back to the caller.
  73.  ***/
  74. Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
  75. {
  76.     OSErr        err;
  77.     short        dummy;
  78.     
  79.     Buf = NewHandle(gBuffSize);
  80.     if ((err = MemError()) != noErr || Buf == nil)
  81.         AlertUser(err, iSetupBuffer);
  82.     MoveHHi (Buf);
  83.     if ((err = MemError()) != noErr)
  84.         AlertUser(err, iSetupBuffer);
  85.     HLock (Buf);
  86.     if ((err = MemError()) != noErr)
  87.         AlertUser(err, iSetupBuffer);
  88.     
  89.     /* The call to SetupSndHeader is done twice in order to make sure that the 
  90.     Header size is set correctly.  */
  91.  
  92.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
  93.     if (err != noErr)
  94.         AlertUser(err, iSetupBuffer);
  95.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, gBuffSize - *HeaderSize, &dummy);
  96.     if (err != noErr)
  97.         AlertUser(err, iSetupBuffer);
  98.         
  99.     return (Buf);
  100. }
  101.  
  102. /***
  103.  * RndRange
  104.  *        Pick a random number from min to max.
  105.  ***/
  106. short RndRange(short min, short max)
  107. {
  108.     unsigned short r = (unsigned short)Random();
  109.     short d = max-min+1;
  110.     return r / (0x10000/d) + min;
  111. }
  112.  
  113.  
  114. /***
  115.  * PickPlayableBuffer
  116.  *        Return the index of a buffer which is ready to be played. It randomly
  117.  *        picks one from the playable buffers, that is, ones which have some
  118.  *        sound in them. If it cant find one, then it returns a value too big.
  119.  ***/
  120. short PickPlayableBuffer(BufInfo *buffers, short max)
  121. {
  122.     short rnd;
  123.     short i;
  124.  
  125.     for(rnd = RndRange(0,max-1), i=0; 
  126.             !buffers[rnd].playable; 
  127.             i++)
  128.         {
  129.         /* try 100 times to find a good buffer before quitting */
  130.         /* oh shuddup, I know its dumb. */
  131.         if (i>100)
  132.             return max+1;
  133.  
  134.         rnd = RndRange(0,max-1);
  135.         }
  136.     return rnd;
  137. }
  138.  
  139.  
  140. /***
  141.  * PickReadableBuffer
  142.  *        Return the index of a buffer which can read some new sounds
  143.  *        First it fills up each buffer, then it randomly picks new ones
  144.  *        once they are all full.
  145.  ***/
  146. short PickReadableBuffer(BufInfo *buffers, short max)
  147. {
  148.     short rnd;
  149.     short i;
  150.  
  151.     for(i=0; i<max; i++)
  152.         {
  153.         if (!buffers[i].playable)
  154.             return i;
  155.         }
  156.  
  157.     return RndRange(0,max-1);
  158. }
  159.  
  160.  
  161. #define Abs(x)        ((x<0)?-(x):x)
  162. #define BlankSpace    0x1000
  163.  
  164. /***
  165.  * TrimBuffer
  166.  *        If the buffer is silent, then return false. Otherwise, reduce the
  167.  *        buffer to just the parts that have sound.
  168.  ***/
  169. long TrimBuffer(Handle buffer, long headerlen)
  170. {
  171.     Boolean result = false;
  172.     long oldBuffSize = GetHandleSize(buffer);
  173.     long oldEnd = 0;
  174.     long oldStart = headerlen;
  175.     long newIndex = 0;
  176.     long copySize = 0;
  177.     long bufByte = 0;
  178.     Boolean firstTime = true;
  179.     Handle tmpbuffer = gExtraBuffer.buffer;
  180.  
  181.     /* copy header into new buffer */
  182.     BlockMove(*buffer, *tmpbuffer, headerlen);
  183.  
  184.     /* determine if the buffer was loud enough */
  185.     /* loud means that a byte in the buffer is far enough away from the */
  186.     /* center to get above the gThreshold */
  187.     for (bufByte=0; bufByte<oldBuffSize-headerlen; bufByte++)
  188.         {
  189.         short delta = 0x80 - (unsigned char)(*buffer)[headerlen + bufByte];
  190.         if (Abs(delta) << 1 > gThreshold)
  191.             {
  192.             /* the first time we hear something, save the previous BlankSpace bytes */
  193.             /* until now. */
  194.             if (firstTime)
  195.                 {
  196.                 /* If we are close to the beginning, copy from the beginning. */
  197.                 /* otherwise, copy blank space until this spot */
  198.                 if (bufByte > BlankSpace)
  199.                     oldStart = bufByte - BlankSpace;
  200.                 else
  201.                     oldStart = 0;
  202.                     
  203.                 /* If we are near the end, copy to the end. */
  204.                 /* otherwise, copy blank space after this spot */
  205.                 oldEnd = bufByte + BlankSpace;
  206.                 if (headerlen + oldEnd > oldBuffSize)
  207.                     oldEnd = oldBuffSize - headerlen;
  208.                 copySize = oldEnd - oldStart;
  209.  
  210.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  211.                 newIndex += copySize;
  212.                 bufByte = oldEnd;
  213.                 firstTime = false;
  214.                 }
  215.             else
  216.                 {
  217.                 /* After the first time, bufByte > BlankSpace. */
  218.                 oldStart = bufByte - BlankSpace;
  219.                 if (oldStart < oldEnd)
  220.                     oldStart = oldEnd+1;
  221.  
  222.                 oldEnd = bufByte + BlankSpace;
  223.                 if (headerlen + oldEnd > oldBuffSize)
  224.                     oldEnd = oldBuffSize - headerlen;
  225.                 copySize = oldEnd - oldStart;
  226.  
  227.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  228.                 newIndex += copySize;
  229.                 bufByte = oldEnd;
  230.                 }
  231.             result = true;
  232.             }
  233.         }
  234.     
  235.     if (result)
  236.         {
  237.         BlockMove(*tmpbuffer, *buffer, newIndex+headerlen);
  238.         return newIndex;
  239.         }
  240.     else
  241.         {
  242.         return 0L;
  243.         }
  244. }
  245.  
  246.  
  247.  
  248. #define TestBit(l,b)    ((l>>b)&0x01)
  249.  
  250. /***
  251.  * InitSoundConfusion
  252.  *        Allocate memory and initialize the sound driver
  253.  ***/
  254. void InitSoundConfusion(void)
  255. {
  256.     Level                myLevel;
  257.     OSErr                err;
  258.     long                feature;
  259.     BufInfo                *Buffers = nil;
  260.     short                i;
  261.     short                numBuffers;
  262.     short                meterState;
  263.     SPBPtr                RecordRec = nil;
  264.     Str255                settingStr;
  265.     long                tmplong;
  266.  
  267.     /* use Gestalt to make sure the app will work */
  268.     err = Gestalt(gestaltSoundAttr, &feature);
  269.     if (err == noErr)
  270.         {
  271.         if (!TestBit(feature, gestaltHasSoundInputDevice))
  272.             {
  273.             AlertUser(0, iNoInput);
  274.             ExitToShell();
  275.             }
  276.         else
  277.             {
  278.  
  279.             struct {
  280.                 short ignore;
  281.                 char majSys;
  282.                 char minSys;
  283.                 } sysVersion;
  284.  
  285.             /* does the machine have stereo? */
  286.             inStereo = TestBit(feature, gestaltStereoCapability);
  287.             
  288.             /* can the machine play and record simultaneously? */
  289.             /* prior to system 7.1, we could assume that if the machine */
  290.             /* had stereo, then it has the better Sound Chip and thus */
  291.             /* can play and record at the same time. 7.1 added the */
  292.             /* gestaltPlayAndRecord bit. I dont have 7.1 yet, but I */
  293.             /* will take Jim Reekes word for it. I put it in my */
  294.             /* GestaltEqu.h file as gestaltPlayAndRecord = 6 */
  295.             err = Gestalt(gestaltSystemVersion, (long*)&sysVersion);
  296.             if (err != noErr)
  297.                 {
  298.                 AlertUser(err, iGestaltFailed);
  299.                 ExitToShell();
  300.                 }
  301.             if (((sysVersion.majSys == 7) && (sysVersion.minSys >= 0x10)) || (sysVersion.majSys > 7))
  302.                 gPlayNRecord = TestBit(feature, gestaltPlayAndRecord);
  303.             else
  304.                 gPlayNRecord = inStereo;
  305.                 
  306.             }
  307.         }
  308.     else
  309.         {
  310.         AlertUser(err, iGestaltFailed);
  311.         ExitToShell();
  312.         }
  313.     
  314.     /* the threshold is a setting in the STR# resource */
  315.     GetIndString(settingStr, rSettingStrings, iThreshold);
  316.     StringToNum(settingStr, &tmplong);
  317.     gThreshold = tmplong;
  318.  
  319.     /* the buffer size is also keps as a string */
  320.     GetIndString(settingStr, rSettingStrings, iBufferSize);
  321.     StringToNum(settingStr, &tmplong);
  322.     gBuffSize = tmplong;
  323.     
  324.     /* There was a time when I was thinking of making an INIT, so I was */
  325.     /* going to keep all globals in a handle, but I changed my mind */
  326.     /* some of these can just be normal global variables, but what */
  327.     /* the heck. */
  328.     globs = (SCGlobals*)NewPtr(sizeof(SCGlobals));
  329.     if ((err = MemError()) != noErr || globs == nil)
  330.         {
  331.         AlertUser(err, iMakingGlobals);
  332.         return;
  333.         }
  334.     globs->fullBuffer = true;
  335.     globs->loudEnough = false;
  336.     globs->bufferGettingFilled = 0;
  337.     globs->leftChan = nil;
  338.     globs->rightChan = nil;
  339.     globs->buffers = nil;
  340.     globs->numBuffers = 0;
  341.     globs->RecordRec = nil;
  342.     globs->direction = iPlay;        /* next time through, it records */
  343.     
  344.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  345.     err = SPBOpenDevice (nil, siWritePermission, &globs->SoundRefNum);
  346.     if (err != noErr)
  347.         {
  348.         AlertUser(err, iOpeningDevice);
  349.         return;
  350.         }
  351.  
  352.     /* Get the sample rate information for the snd header */
  353.     err = SPBGetDeviceInfo (globs->SoundRefNum,siSampleRate, (Ptr) &globs->sampleRate);
  354.     if (err != noErr)
  355.         {
  356.         AlertUser(err, iGettingRate);
  357.         return;
  358.         }
  359.     
  360.     /* build the RecordRec pointer and fill in the fields */
  361.     RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
  362.     if ((err = MemError()) != noErr || RecordRec == nil)
  363.         {
  364.         AlertUser(err, iMakingRecordRec);
  365.         return;
  366.         }
  367.     globs->RecordRec = RecordRec;
  368.  
  369.     /* how many buffers can we make? (leave space for an extra buffer and then some memory) */
  370.     numBuffers = ((FreeMem() - kExtraMem) / gBuffSize) - 1;
  371.     if (numBuffers < 1)
  372.         {
  373.         AlertUser(0, iMemory);
  374.         return;
  375.         }
  376.     
  377.     Buffers = (BufInfo*)NewPtr(sizeof(BufInfo)*numBuffers);
  378.     if ((err = MemError()) != noErr || Buffers == nil)
  379.         {
  380.         AlertUser(err, iAllocatingBuffers);
  381.         return;
  382.         }
  383.     globs->buffers = Buffers;
  384.     globs->numBuffers = numBuffers;
  385.         
  386.     
  387.     /* build the snd buffers */
  388.     for (i=0; i<numBuffers; i++)
  389.         {
  390.         Buffers[i].buffer = SetUpSounds(Buffers[i].buffer, &Buffers[i].headerlength, globs->sampleRate);
  391.         Buffers[i].buffSize = gBuffSize - Buffers[i].headerlength;
  392.         Buffers[i].playable = false;
  393.         }
  394.  
  395.     gExtraBuffer.buffer = SetUpSounds(gExtraBuffer.buffer, &gExtraBuffer.headerlength, globs->sampleRate);
  396.  
  397.     RecordRec->inRefNum = globs->SoundRefNum;
  398.     RecordRec->count =  gBuffSize - Buffers[0].headerlength;
  399.     RecordRec->milliseconds = 0;
  400.     RecordRec->bufferLength = gBuffSize - Buffers[0].headerlength;
  401.     RecordRec->bufferPtr = (Ptr) ((*Buffers[0].buffer) + Buffers[0].headerlength);
  402.     RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
  403.     RecordRec->interruptRoutine = nil;
  404.     RecordRec->userLong = (long)globs;
  405.     RecordRec->error = 0;
  406.     RecordRec->unused1 = 0;
  407.     
  408.  
  409. #ifdef IMOVEDTHISINTORESETCHANNELS
  410.  
  411.     /* if the machine has stereo, then open the right and left channels */
  412.     if (inStereo)
  413.         {
  414.         /* open the left channel which I can play from */
  415.         globs->leftChan = nil;
  416.         err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
  417.         if (err != noErr)
  418.             {
  419.             AlertUser(err, iMakingLeft);
  420.             return;
  421.             }
  422.         
  423.         /* open the right channel which I can play from */
  424.         globs->rightChan = nil;
  425.         err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
  426.         if (err != noErr)
  427.             {
  428.             AlertUser(err, iMakingRight);
  429.             return;
  430.             }
  431.         }
  432.     else
  433.         {
  434.         /* open a mono channel and keep in the 'left' variable */
  435.         globs->leftChan = nil;
  436.         err = SndNewChannel (&globs->leftChan, sampledSynth, initMono, nil);
  437.         if (err != noErr)
  438.             {
  439.             AlertUser(err, iMakingMono);
  440.             return;
  441.             }
  442.         }
  443. #endif
  444.  
  445.     globs->firstTime = true;
  446.     
  447. //    ResetChannels();
  448.     
  449. }
  450.  
  451.  
  452. /***
  453.  * ResetChannels
  454.  *
  455.  *        Close (if not already) and then open the sound channel(s)
  456.  ***/
  457. void ResetChannels()
  458. {
  459.     SndCommand        mycmd;
  460.     OSErr            err;
  461.     static Boolean channelsExist = false;
  462.     
  463.     if (channelsExist)
  464.         {
  465.         /* stop playing sounds now */
  466.         mycmd.cmd = quietCmd;
  467.         mycmd.param1 = 0;
  468.         mycmd.param2 = 0;
  469.         if (globs->leftChan)
  470.             {
  471.             /* tell the left channel to shut up */
  472.             err = SndDoImmediate (globs->leftChan, &mycmd);
  473.             if (err != noErr)
  474.                 {
  475.                 AlertUser(err, iClosing);
  476.                 return;
  477.                 }
  478.                 
  479.             /* kill the left channel */
  480.             err = SndDisposeChannel (globs->leftChan,false);
  481.             if (err != noErr)
  482.                 {
  483.                 AlertUser(err, iClosing);
  484.                 return;
  485.                 }
  486.             }
  487.         if (inStereo && globs->rightChan)
  488.             {
  489.             /* tell the right channel to shut up */
  490.             err = SndDoImmediate (globs->rightChan, &mycmd);
  491.             if (err != noErr)
  492.                 {
  493.                 AlertUser(err, iClosing);
  494.                 return;
  495.                 }
  496.     
  497.             /* kill the right channel */
  498.             err = SndDisposeChannel (globs->rightChan,false);
  499.             if (err != noErr)
  500.                 {
  501.                 AlertUser(err, iClosing);
  502.                 return;
  503.                 }
  504.             }
  505.         }
  506.  
  507.     /* if the machine has stereo, then open the right and left channels */
  508.     if (inStereo)
  509.         {
  510.         /* open the left channel which I can play from */
  511.         globs->leftChan = nil;
  512.         err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
  513.         if (err != noErr)
  514.             {
  515.             AlertUser(err, iMakingLeft);
  516.             return;
  517.             }
  518.         
  519.         /* open the right channel which I can play from */
  520.         globs->rightChan = nil;
  521.         err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
  522.         if (err != noErr)
  523.             {
  524.             AlertUser(err, iMakingRight);
  525.             return;
  526.             }
  527.         }
  528.     else
  529.         {
  530.         /* open a mono channel and keep in the 'left' variable */
  531.         globs->leftChan = nil;
  532.         err = SndNewChannel (&globs->leftChan, sampledSynth, initMono, nil);
  533.         if (err != noErr)
  534.             {
  535.             AlertUser(err, iMakingMono);
  536.             return;
  537.             }
  538.         }
  539.  
  540.     channelsExist = true;
  541. }
  542.  
  543.  
  544. /***
  545.  * ConfuseSound
  546.  *        This is called during idle time from the event loop.
  547.  *        It starts recording from a buffer and playing from another one.
  548.  ***/
  549. void ConfuseSound(void)
  550. {
  551.     short            dummy;
  552.     OSErr            err;
  553.     BufInfo            *Buffers;
  554.     short            i;
  555.     SndCommand        mycmd;
  556.     long            bufByte;
  557.     short            bufNum;
  558.     Boolean            stillWorking = false;
  559.     Boolean            stillPlaying = false;
  560.     Boolean            stillRecording = false;
  561.     SCStatus        chanStatL;
  562.     SCStatus        chanStatR;
  563.     static    unsigned long    lastResetTime = 0L;
  564.     unsigned long    currTime;
  565.     
  566.     GetDateTime(&currTime);
  567.     
  568.     if (currTime - lastResetTime > kResetInterval)
  569.         {
  570.         ResetChannels();
  571.         lastResetTime = currTime;
  572.         }
  573.     
  574.     /* easier access to the buffers */
  575.     Buffers = globs->buffers;
  576.  
  577.     /* some stuff to prevent us from playing and recording simultaneously */
  578.     /* on a machine that is incapable of it. */
  579.     if (globs->firstTime)
  580.         {
  581.         stillPlaying = false;
  582.         stillRecording = false;
  583.         }
  584.     else 
  585.         {
  586.         if (globs->direction == iPlay || gPlayNRecord)
  587.             {
  588.             err = SndChannelStatus(globs->leftChan, sizeof(SCStatus), &chanStatL);
  589.             if (err != noErr)
  590.                 AlertUser(err, iPlaying);
  591.             stillPlaying = chanStatL.scChannelBusy;
  592.  
  593.             if (inStereo && !stillPlaying)
  594.                 {
  595.                 err = SndChannelStatus(globs->rightChan, sizeof(SCStatus), &chanStatR);
  596.                 if (err != noErr)
  597.                     AlertUser(err, iPlaying);
  598.                 stillPlaying = chanStatR.scChannelBusy;
  599.                 }
  600.                 
  601.             }
  602.         if (globs->direction == iRecord || gPlayNRecord)
  603.             {
  604.             stillRecording = !globs->fullBuffer;
  605.             }
  606.         }
  607.     
  608.     stillWorking = stillPlaying || stillRecording;
  609.     
  610.     /* if we cant play and record, then exit if we are in the middle of */
  611.     /* playing or recording. we can pass through as soon as the process is done */
  612.     if ((!gPlayNRecord && stillWorking) || (stillPlaying && stillRecording))
  613.         return;
  614.  
  615.  
  616.     /* if this is the first time through, then prepare to record into a buffer */
  617.     /* if a buffer recording has finished (ie, the buffer is full), then prepare */
  618.     /* to record into another buffer */
  619.     if (!stillRecording)
  620.         {
  621.         globs->fullBuffer = false;
  622.     
  623.         if (!globs->firstTime && (gPlayNRecord || globs->direction == iRecord))
  624.             {
  625.             long newSize;
  626.             bufNum = globs->bufferGettingFilled;
  627.             
  628.             /* trim the buffer by removing silent (quiet) sections */
  629.             newSize = TrimBuffer(Buffers[bufNum].buffer, Buffers[bufNum].headerlength);
  630.             Buffers[bufNum].playable = globs->loudEnough = (newSize > 0);
  631.             Buffers[bufNum].buffSize = newSize;
  632.     
  633.             /* if the buffer was loud enough, then prepare to record the next readable buffer */
  634.             if (globs->loudEnough)
  635.                 {
  636.                 short nextbuf = PickReadableBuffer(Buffers, globs->numBuffers);
  637.                 globs->bufferGettingFilled = nextbuf;
  638.                 globs->loudEnough = false;
  639.                 Buffers[nextbuf].playable = false;
  640.                 HUnlock(Buffers[nextbuf].buffer);
  641.                 SetHandleSize(Buffers[nextbuf].buffer, gBuffSize);
  642.                 MoveHHi(Buffers[nextbuf].buffer);
  643.                 HLock(Buffers[nextbuf].buffer);
  644.                 globs->RecordRec->bufferPtr = (*Buffers[nextbuf].buffer) + Buffers[nextbuf].headerlength;
  645.                 }
  646.             }
  647.                 
  648.         globs->firstTime = false;
  649.  
  650.         if (gPlayNRecord)
  651.             {
  652.             /* start recording into the buffer asynchronously */
  653.             err = SPBRecord (globs->RecordRec, true); // start recording
  654.             if (err != noErr)
  655.                 AlertUser(err, iRecording);
  656.             }
  657.         }
  658.     
  659.     if (gPlayNRecord && !stillPlaying)
  660.         {
  661.         /* pick a buffer to play from */    
  662.         i = PickPlayableBuffer(Buffers, globs->numBuffers);
  663.         if (i<=globs->numBuffers)
  664.             {
  665.             long bsize = Buffers[i].buffSize;
  666.             err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
  667.             if (err != noErr)
  668.                 AlertUser(err, iPlaying);
  669.             /* play it asynchronously */
  670.             BufPlay(Buffers[i].buffer, 
  671.                     inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
  672.             }
  673.         }
  674.     
  675.     
  676.     /* if we cant play and record simultaneously, then do t`he one that */
  677.     /* we werent doing last. in other words, take turns playing and recording */
  678.     if (!gPlayNRecord)
  679.         {
  680.         if (globs->direction == iPlay)
  681.             {
  682.             globs->fullBuffer = false;
  683.             /* start recording into the buffer asynchronously */
  684.             err = SPBRecord (globs->RecordRec, true); // start recording
  685.             if (err != noErr)
  686.                 AlertUser(err, iRecording);
  687.             globs->direction = iRecord;
  688.             }
  689.         else /* if (globs->direction == iRecord) */
  690.             {
  691.             /* pick a buffer to play from */    
  692.             i = PickPlayableBuffer(Buffers, globs->numBuffers);
  693.             if (i<=globs->numBuffers)
  694.                 {
  695.                 long bsize = Buffers[i].buffSize;
  696.                 err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
  697.                 if (err != noErr)
  698.                     AlertUser(err, iPlaying);
  699.                 /* play it asynchronously */
  700.                 BufPlay(Buffers[i].buffer, 
  701.                         inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
  702.                 }
  703.             globs->direction = iPlay;
  704.             globs->fullBuffer = true;
  705.             }
  706.         }
  707. }
  708.  
  709.  
  710. /***
  711.  * StopConfusion
  712.  *        Deallocate memory and close stuff.
  713.  ***/
  714. void StopConfusion(void)
  715. {
  716.     OSErr            err;
  717.     SndCommand        mycmd;
  718.     short            i;
  719.  
  720.     if (!globs)
  721.         return;
  722.  
  723.     /* stop playing sounds now */
  724.     mycmd.cmd = quietCmd;
  725.     mycmd.param1 = 0;
  726.     mycmd.param2 = 0;
  727.     if (globs->leftChan)
  728.         {
  729.         /* tell the left channel to shut up */
  730.         err = SndDoImmediate (globs->leftChan, &mycmd);
  731.         if (err != noErr)
  732.             AlertUser(err, iClosing);
  733.             
  734.         /* kill the left channel */
  735.         err = SndDisposeChannel (globs->leftChan,false);
  736.         if (err != noErr)
  737.             AlertUser(err, iClosing);
  738.         }
  739.     if (inStereo && globs->rightChan)
  740.         {
  741.         /* tell the right channel to shut up */
  742.         err = SndDoImmediate (globs->rightChan, &mycmd);
  743.         if (err != noErr)
  744.             AlertUser(err, iClosing);
  745.  
  746.         /* kill the right channel */
  747.         err = SndDisposeChannel (globs->rightChan,false);
  748.         if (err != noErr)
  749.             AlertUser(err, iClosing);
  750.         }
  751.  
  752.     if (globs->SoundRefNum != 0)
  753.         {
  754.         /* close the input device */
  755.         SPBCloseDevice (globs->SoundRefNum);
  756.         }
  757.     
  758.     if (globs->buffers)
  759.         {
  760.         /* kill the snd buffers */
  761.         for (i=0; i<globs->numBuffers; i++)
  762.             {
  763.             if (globs->buffers[i].buffer)
  764.                 {
  765.                 HUnlock(globs->buffers[i].buffer);
  766.                 DisposeHandle(globs->buffers[i].buffer);
  767.                 }
  768.             }
  769.         }
  770.  
  771.     if (gExtraBuffer.buffer)
  772.         {
  773.         HUnlock(gExtraBuffer.buffer);
  774.         DisposeHandle(gExtraBuffer.buffer);
  775.         }
  776.  
  777.     /* kill the buffers */
  778.     DisposePtr ((Ptr) globs->buffers);
  779.     DisposePtr ((Ptr) globs->RecordRec);
  780.     DisposePtr ((Ptr) globs);
  781. }
  782.  
  783.  
  784. /**********************************/
  785.  
  786. void DoAbout()
  787. {
  788.     Str31 numBufsStr;
  789.     Str31 numFilledStr;
  790.     short numFilled = 0;
  791.     short i;
  792.     short itemHit;
  793.     DialogPtr dlg;
  794.     
  795.     for (i=0; i<globs->numBuffers; i++)
  796.         {
  797.         if (globs->buffers[i].playable)
  798.             numFilled++;
  799.         }
  800.     NumToString((long)globs->numBuffers, numBufsStr);
  801.     NumToString((long)numFilled, numFilledStr);
  802.     ParamText(numBufsStr, numFilledStr, "", "");
  803.  
  804.     dlg = GetNewDialog(rAboutAlert, NULL, (WindowPtr)-1L);
  805.     ModalDialog(NULL, &itemHit);
  806.     DisposDialog(dlg);
  807. }
  808.  
  809.  
  810. /***
  811.  * MyRecComp
  812.  *
  813.  * This is the Completion Routine which is called every time the buffer,
  814.  * which is being recorded into, is full.
  815.  ***/
  816. pascal void MyRecComp (SPBPtr inParamPtr)
  817. {
  818.     SCGlobals *glob;
  819.     
  820.     glob = (SCGlobals*)inParamPtr->userLong;
  821.     glob->fullBuffer = true;
  822.  
  823.     if (inParamPtr->error != noErr)
  824.         {
  825.         AlertUser(inParamPtr->error, iPlaying);
  826.         }
  827.  
  828.     return;
  829. }
  830.